/**
 * @author Huynh
 * 
 * The class 'Game' describes structure of a game
 */
package model;

import controller.Constant;
import android.util.Log;
import android.util.Pair;

public class Game {
	private BoardStructure board;
	private int turn = 0;
	private int gameType;
	private Player firstPlayer;
	private Player secondPlayer;

	// This array store the steps that players make, which serves as a game
	// diary.
	private int[] gameDiary = new int[42];
	private int currentMove = 0;

	/**
	 * Constructor. Parameter: The number indicate the type of game.
	 * 
	 * @param gameType
	 *            the game type (Human vs human, ...)
	 */
	public Game(int type) {
		this.gameType = type;
		board = new BoardStructure();
		changeType(type);
	}

	/**
	 * Get the user's next move knowing the key that the user press. If this is
	 * the comp's turn, ignore this key. If this is the human player's turn,
	 * just make the move based on the key.
	 * 
	 * @param column
	 *            The column that the player intended to put a marble in.
	 * @return The column and the row of the selected cell.
	 */
	public Pair<Integer, Integer> move(int column) {
		// If this is a human player, then wait for another move. If this is a
		// computer player,
		// then this is not the computer's decision, so continue.
		if (!isValidMove(column)
				&& this.getInstanceType(this.getTurn()) == Constant.IS_HUMAN) {
			Log.i("move", "Invalid column");
			return new Pair<Integer, Integer>(-1, -1);
		}

		int chosenColumn; // The column that that user will choose.
		if (turn == 0) {
			chosenColumn = firstPlayer.getMove(column, this);
		} else {
			chosenColumn = secondPlayer.getMove(column, this);
		}

		// update the game status.
		return new Pair<Integer, Integer>(chosenColumn,
				upDateGameStatus(chosenColumn));
	}

	/**
	 * Is the game finished after a user selects a column? Change the turn after
	 * this move, update the remaining squares.
	 * 
	 * @return
	 */
	private int upDateGameStatus(int column) {
		// Update the diary.
		gameDiary[currentMove] = column;
		currentMove++;

		if (isOver()) {// The game is over, change nothing.
			return -1;
		}

		// Find the cell that the user will put the marble.
		int row = board.getRow(column);

		// Put a marble at the cell.
		if (row < 0) {// Full column.
			return -1;
		}

		if (turn == 0)
			board.setCell(column, row, 0);
		else
			board.setCell(column, row, 1);

		// This is the other player's turn.
		turn = 1 - turn;

		return row;
	}

	/**
	 * Check if the game is finished.
	 */
	public Boolean isOver() {
		return board.isOver();
	}

	/**
	 * Get the current player's turn.
	 */
	public int getTurn() {
		return turn;
	}

	/**
	 * Return the next turn.
	 */
	public int getPreviousTurn() {
		return (1 - turn);
	}

	/**
	 * Check if a particular move that an user make is valid or not. The move is
	 * invalid if the column is out of range [0-6] or the column is full
	 * already.
	 * 
	 * @param column
	 * @return
	 */
	public boolean isValidMove(int column) {
		if (column < 0 || column > 6 || board.getRow(column) == -1 || isOver())
			return false;
		else
			return true;
	}

	public int getWinner() {
		return board.getWinner();
	}

	/** Get the player type of the player having a certain turn */
	public Boolean getInstanceType(int turn) {
		Player instance;
		if (turn == 0) {
			instance = firstPlayer;
		} else {
			instance = secondPlayer;
		}
		return (instance instanceof HumanPlayer);
	}

	/**
	 * Return the board of the game.
	 */
	public BoardStructure getBoard() {
		return this.board;
	}

	/**
	 * make copy of this game.
	 */
	public Game makeGame() {
		Game g = new Game(Constant.HUMAN_VS_HUMAN);
		g.board = this.board.makeBoard();
		g.turn = this.turn;
		return g;
	}// End function makeGame.

	/**
	 * Return the type of the game.
	 */
	public int getType() {
		return gameType;
	}

	/**
	 * Return a goodness for a player.
	 */
	public int getGoodness(int player) {
		return board.getGoodness(player);
	}

	/**
	 * Return the game diary. TODO: Make a copy of gameDiary before return, so
	 * user of this function will not be able to modify the game's diary.
	 */
	public Pair<int[], Integer> getDiary() {
		return new Pair<int[], Integer>(gameDiary, currentMove);
	}

	public void changeType(int type) {
		this.gameType = type;
		switch (gameType) {
		case Constant.HUMAN_VS_COMP:
			firstPlayer = new HumanPlayer();
			secondPlayer = new CompPlayer(this, 1);
			break;
		case Constant.HUMAN_VS_HUMAN:
			firstPlayer = new HumanPlayer();
			secondPlayer = new HumanPlayer();
			break;
		case Constant.COMP_VS_HUMAN:
			firstPlayer = new CompPlayer(this, 0);
			secondPlayer = new HumanPlayer();
			break;
		case Constant.ONLINE_GUEST:
		case Constant.ONLINE_HOST:
			firstPlayer = new HumanPlayer();
			secondPlayer = new HumanPlayer();
			break;
		default:
			break;
		}

	}
}
